package com.m3958.logcollector.controller;

import java.io.IOException;
import java.util.regex.Pattern;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * copy some code from @see {@link BasicAuthenticationFilter}
 * 
 * @author jianglibo@gmail.com
 *         2015年8月20日
 *
 */
public class ShBasicFilter extends OncePerRequestFilter {

    public static final String KP_REQUEST_ATTR_KEY = "_KP_REQUEST_ATTR_KEY";

    static final String FILTER_APPLIED = "__shbasic_filter_applied";

	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
	
    @Value("${spring.data.rest.base-path}")
    private String apiPrefix;

    private String createTaskListdUri;
    
    private Pattern getTaskListTasksdPtn;
    
    private Pattern getTaskListPtn;

    private String getUnporcessedTaskItemUri;
    
    private Pattern putTaskItemPtn;
    
    private String TASKITEMS;

    @Override
    protected void initFilterBean() throws ServletException {
        super.initFilterBean();
//        createTaskListdUri = apiPrefix + ResourceNameConstants.TASK_LIST_PATH;
//        getUnporcessedTaskItemUri = apiPrefix + ResourceNameConstants.TASK_ITEM_PATH + "/search/queueing";
//        putTaskItemPtn = Pattern.compile(apiPrefix + ResourceNameConstants.TASK_ITEM_PATH + "/\\d+$");
//        getTaskListPtn = Pattern.compile(apiPrefix + ResourceNameConstants.TASK_LIST_PATH + "/\\d+$");
//        getTaskListTasksdPtn = Pattern.compile(apiPrefix + ResourceNameConstants.TASK_LIST_PATH + "/\\d+/tasks");
//        TASKITEMS = apiPrefix + ResourceNameConstants.TASK_ITEM_PATH;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        @SuppressWarnings("unused")
        boolean isPost = "POST".equals(request.getMethod());
        
        boolean isGet = "GET".equals(request.getMethod());
        
        boolean isPut = "PUT".equals(request.getMethod()); 
        
        NeedFilter needFilter = NeedFilter.SKIP;
        
        String uri = request.getRequestURI();

        if (createTaskListdUri.equals(uri)) {
            needFilter = NeedFilter.MAYBE;
        } else if(isGet && getUnporcessedTaskItemUri.equals(uri)) {
            needFilter = NeedFilter.MUST;
        } else if (isPut && putTaskItemPtn.matcher(uri).matches()) {
            needFilter = NeedFilter.MAYBE;
        } else if (isGet && putTaskItemPtn.matcher(uri).matches()) {
            needFilter = NeedFilter.MAYBE;
        } else if (isGet && getTaskListTasksdPtn.matcher(uri).matches()) {
            needFilter = NeedFilter.MUST;
        } else if (isGet && getTaskListPtn.matcher(uri).matches()) {
            needFilter = NeedFilter.MAYBE;
        } else if ("/v1/taskitems".equals(uri)) {
            needFilter = NeedFilter.MAYBE;
        } else if (TASKITEMS.equals(uri)) {
            needFilter = NeedFilter.MAYBE;
        }
        
        switch (needFilter) {
        case MUST:
            try {
                processBasicLogin(request);
            } catch (AccessDeniedException e) {
                send403(response);
                return;
            }
            break;
        case MAYBE:
            try {
                processBasicLogin(request);
            } catch (AccessDeniedException e) {
            }
        default:
            break;
        }
        
        chain.doFilter(request, response);
    }
    
    public static enum NeedFilter {
        MUST, SKIP, MAYBE
    }

    private void processBasicLogin(HttpServletRequest request) throws AccessDeniedException, IOException {
        String header = request.getHeader("Authorization");

        if (header == null || !header.startsWith("Basic ")) {
            throw new AccessDeniedException("no basic header.");
        }

//        try {
////            SecurityContextHolder.clearContext();
//            String[] tokens = extractAndDecodeHeader(header, request);
//            assert tokens.length == 2;
//            Keypair kp = kpRepo.byIdAndSecret(tokens[0], tokens[1]);
//            if (kp == null) {
//                throw new AccessDeniedException("wroing keypair");
//            } else if (kp.isArchived()) {
//                throw new AccessDeniedException("archived keypair");
//            }
//            ScanhubLoginAuthenticationToken uat = new ScanhubLoginAuthenticationToken(new ScanhubUserVo(kp.getCreator()));
//            
//			uat.setDetails(authenticationDetailsSource.buildDetails(request));
//            uat.setAuthenticated(true);
//            SecurityContextHolder.getContext().setAuthentication(uat);
////            ScanhubSecurityUtil.doLogin(kp.getCreator());
//            request.setAttribute(KP_REQUEST_ATTR_KEY, kp);
//        } catch (AuthenticationException failed) {
//            SecurityContextHolder.clearContext();
//            throw new AccessDeniedException("authentication exception.");
//        }
    }

    private void send403(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().close();
    }

    /**
     * Decodes the header into a username and password.
     *
     * @throws BadCredentialsException
     *             if the Basic header is not present or is not valid
     *             Base64
     */
    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {

        byte[] base64Token = header.substring(6).getBytes("UTF-8");
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }

        String token = new String(decoded, "UTF-8");

        int delim = token.indexOf(":");

        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        }
        return new String[] { token.substring(0, delim), token.substring(delim + 1) };
    }

}
